home *** CD-ROM | disk | FTP | other *** search
- //-----------------------------------------------------------------------------
- // File: flexscrollbar.cpp
- //
- // Desc: Implements CFlexScrollBar (derived from CFlexWnd), a scroll bar
- // control similar to a Windows scroll bar.
- //
- // Copyright (C) 1999-2001 Microsoft Corporation. All Rights Reserved.
- //-----------------------------------------------------------------------------
-
- #include "common.hpp"
-
-
- CFlexScrollBar::CFlexScrollBar() :
- m_nMin(0),
- m_nMax(0),
- m_nPage(25),
- m_nPos(25),
- m_bVert(TRUE),
- m_hWndNotify(NULL),
- m_bValid(FALSE),
- m_bCapture(FALSE),
- m_bDragging(FALSE),
- m_code(SB_ENDSCROLL),
- m_rgbBk(RGB(0,0,0)),
- m_rgbFill(RGB(0,0,255)),
- m_rgbLine(RGB(0,255,255))
- {
- }
-
- CFlexScrollBar::~CFlexScrollBar()
- {
- }
-
- CFlexScrollBar *CreateFlexScrollBar(FLEXSCROLLBARCREATESTRUCT *pcs)
- {
- CFlexScrollBar *psb = new CFlexScrollBar;
-
- if (psb && psb->Create(pcs))
- return psb;
-
- delete psb;
- return NULL;
- }
-
- BOOL CFlexScrollBar::Create(FLEXSCROLLBARCREATESTRUCT *pcs)
- {
- if (this == NULL)
- return FALSE;
-
- if (pcs == NULL)
- return FALSE;
-
- if (pcs->dwSize != sizeof(FLEXSCROLLBARCREATESTRUCT))
- return FALSE;
-
- if (pcs->min > pcs->max)
- return FALSE;
-
- int range = pcs->max - pcs->min;
-
- if (pcs->page > range)
- return FALSE;
-
- m_bVert = ( pcs->dwFlags & FSBF_VERT ) == FSBF_VERT;
-
- SetValues(pcs->min, pcs->max, pcs->page, pcs->pos);
-
- m_hWndNotify = pcs->hWndNotify ? pcs->hWndNotify : pcs->hWndParent;
-
- if (!CFlexWnd::Create(pcs->hWndParent, pcs->rect, pcs->bVisible))
- return FALSE;
-
- Calc();
-
- // TODO: make sure that creation sends no notifications.
- // all initial notifications should be sent here.
-
- return TRUE;
- }
-
- int CFlexScrollBar::GetLineAdjust()
- {
- return 1;
- }
-
- int CFlexScrollBar::GetPageAdjust()
- {
- return m_nPage > 1 ? m_nPage - 1 : 1;
- }
-
- void CFlexScrollBar::AdjustPos(int adj, BOOL bForceCalc)
- {
- int old = m_nPos;
-
- m_nPos += adj;
-
- if (m_nPos < m_nMin)
- m_nPos = m_nMin;
- if (m_nPos > m_nMax - m_nPage)
- m_nPos = m_nMax - m_nPage;
-
- if (m_nPos == old && !bForceCalc)
- return;
-
- if (Calc())
- Invalidate();
- }
-
- BOOL CFlexScrollBar::FailCalc(BOOL bOldValid)
- {
- m_bValid = FALSE;
- if (bOldValid)
- Invalidate();
- return m_bValid;
- }
-
- BOOL CFlexScrollBar::Calc()
- {
- BOOL bOldValid = m_bValid;
- #define FAIL return FailCalc(bOldValid)
-
- if (!m_hWnd)
- FAIL;
-
- SRECT zero;
- m_rectLineUp = zero;
- m_rectPageUp = zero;
- m_rectTrack = zero;
- m_rectThumb = zero;
- m_rectPageDown = zero;
- m_rectLineDown = zero;
-
- SPOINT size = GetClientSize();
-
- int ord = m_bVert ? 1 : 0;
- int nord = m_bVert ? 0 : 1;
- int length = size.a[ord];
- int width = size.a[nord];
- int arrowlen = width;
-
- if (width < 1 || length < 2)
- FAIL;
-
- int tracklen = length - arrowlen * 2;
- int trackofs = arrowlen;
-
- BOOL bOverlappingArrows = tracklen < -1;
- int overlap = !bOverlappingArrows ? 0 : -tracklen;
-
- SRECT up, down, track, thumb, temp;
-
- if (overlap > 1)
- {
- int mid = length / 2;
- up.lr.a[nord] = width;
- up.lr.a[ord] = mid;
- down.ul.a[ord] = mid;
- down.lr.a[ord] = length;
- down.lr.a[nord] = width;
- m_rectLineUp = up;
- m_rectLineDown = down;
- return m_bValid = TRUE;
- }
-
- up.lr.a[nord] = width;
- up.lr.a[ord] = arrowlen;
- down.lr.a[nord] = width;
- down.ul.a[ord] = length - arrowlen;
- down.lr.a[ord] = length;
- m_rectLineUp = up;
- m_rectLineDown = down;
-
- int tmin = up.lr.a[ord];
- int tmax = down.ul.a[ord];
- int trange = tmax - tmin;
- int range = m_nMax - m_nMin;
- assert(trange > 0);
- if (!(range > 0) || !(trange > 0))
- return m_bValid = TRUE;
-
- track.ul.a[ord] = tmin;
- track.lr.a[nord] = width;
- track.lr.a[ord] = tmax;
- m_rectTrack = track;
-
- const int minthumblen = 3;
- int thumblen = MulDiv(m_nPage, trange, range);
- if (thumblen < minthumblen)
- thumblen = minthumblen;
-
- int thumbrange = trange - thumblen /*+ 1*/;
- int pagerange = range - m_nPage;
- if (!(pagerange > 1) || !(thumbrange > 1))
- return m_bValid = TRUE;
- int logpos = m_bDragging ? m_nPreDragPos : m_nPos;
- int thumbpos = MulDiv(logpos - m_nMin, thumbrange, pagerange) + tmin;
- if (m_bDragging)
- {
- SPOINT rp = m_point, rs = m_startpoint;
- int rdelta = rp.a[ord] - rs.a[ord];
- thumbpos += rdelta;
- if (thumbpos < tmin)
- thumbpos = tmin;
- if (thumbpos > tmax - thumblen)
- thumbpos = tmax - thumblen;
- m_nThumbPos = MulDiv(thumbpos - tmin, pagerange, thumbrange) + m_nMin;
- if (m_nThumbPos < m_nMin)
- m_nThumbPos = m_nMin;
- if (m_nThumbPos > m_nMax - m_nPage)
- m_nThumbPos = m_nMax - m_nPage;
- }
-
- thumb.ul.a[ord] = thumbpos;
- thumb.lr.a[nord] = width;
- thumb.lr.a[ord] = thumbpos + thumblen;
- m_rectThumb = thumb;
-
- temp = track;
- temp.lr.a[ord] = thumb.ul.a[ord];
- if (temp.lr.a[ord] > temp.ul.a[ord])
- m_rectPageUp = temp;
-
- temp = track;
- temp.ul.a[ord] = thumb.lr.a[ord];
- if (temp.lr.a[ord] > temp.ul.a[ord])
- m_rectPageDown = temp;
-
- return m_bValid = TRUE;
- #undef FAIL
- }
-
- void CFlexScrollBar::SetValues(int min, int max, int page, int pos)
- {
- m_nMin = min < max ? min : max;
- m_nMax = max > min ? max : min;
- m_nPage = page;
- AdjustPos(pos - m_nPos, TRUE);
- }
-
- static BOOL UseRect(const RECT &rect)
- {
- if (rect.left >= rect.right || rect.bottom <= rect.top)
- return FALSE;
- return TRUE;
- }
-
- static void Rectangle(HDC hDC, RECT rect)
- {
- if (!UseRect(rect))
- return;
-
- Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);
- }
-
- void CFlexScrollBar::OnPaint(HDC hDC)
- {
- HDC hBDC = NULL, hODC = NULL;
- CBitmap *pbm = NULL;
-
- if (!InRenderMode())
- {
- hODC = hDC;
- pbm = CBitmap::Create(GetClientSize(), RGB(0,0,0), hDC);
- if (pbm != NULL)
- {
- hBDC = pbm->BeginPaintInto();
- if (hBDC != NULL)
- {
- hDC = hBDC;
- }
- }
- }
-
- InternalPaint(hDC);
-
- if (!InRenderMode())
- {
- if (pbm != NULL)
- {
- if (hBDC != NULL)
- {
- pbm->EndPaintInto(hBDC);
- pbm->Draw(hODC);
- }
- delete pbm;
- }
- }
- }
-
- void CFlexScrollBar::InternalPaint(HDC hDC)
- {
- HGDIOBJ hPen, hOldPen, hBrush, hOldBrush;
- hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbBk);
- if (hPen != NULL)
- {
- hOldPen = SelectObject(hDC, hPen),
- hOldBrush = SelectObject(hDC, GetStockObject(BLACK_BRUSH));
-
- Rectangle(hDC, m_rectPageUp);
- Rectangle(hDC, m_rectPageDown);
- Rectangle(hDC, m_rectLineUp);
- Rectangle(hDC, m_rectLineDown);
-
- SelectObject(hDC, hOldPen);
- DeleteObject(hPen);
-
- hBrush = (HGDIOBJ)CreateSolidBrush(m_rgbFill);
- if (hBrush != NULL)
- {
- SelectObject(hDC, (HGDIOBJ)hBrush);
-
- hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbFill);
- if (hPen != NULL)
- {
- SelectObject(hDC, hPen);
-
- Rectangle(hDC, m_rectThumb);
-
- SelectObject(hDC, hOldPen);
- DeleteObject(hPen);
- }
-
- hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbLine);
- if (hPen != NULL)
- {
- SelectObject(hDC, hPen);
-
- // draw the two arrows for this scrollbar
- for (int i = 0; i < 2; i++)
- DrawArrow(hDC, i ? m_rectLineUp : m_rectLineDown, m_bVert, i);
-
- #if 0
- // draw the two arrows for this scrollbar
- for (int i = 0; i < 2; i++)
- {
- const RECT &rect = i == 0 ? m_rectLineUp : m_rectLineDown;
- SRECT srect = rect;
- srect.right--;
- srect.bottom--;
- int ord = m_bVert ? 1 : 0;
- int nord = m_bVert ? 0 : 1;
- SPOINT p(i ? srect.lr : srect.ul), b(i ? srect.ul : srect.lr);
- b.a[ord] += 2 * i - 1;
- SPOINT t = p;
- t.a[nord] = (p.a[nord] + b.a[nord]) / 2;
- SPOINT u;
- u.a[ord] = b.a[ord];
- u.a[nord] = p.a[nord];
- POINT poly[] = { {t.x, t.y}, {u.x, u.y}, {b.x, b.y} };
- Polygon(hDC, poly, 3);
- }
- #endif
-
- SelectObject(hDC, hOldPen);
- DeleteObject(hPen);
- }
-
- SelectObject(hDC, hOldBrush);
- DeleteObject(hBrush);
- }
- }
- }
-
- BOOL InRect(const RECT &rect, POINT point)
- {
- return UseRect(rect) && PtInRect(&rect, point);
- }
-
- LRESULT CFlexScrollBar::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
- {
- switch (msg)
- {
- case WM_SIZE:
- Calc();
- Invalidate();
- return 0;
-
- // make sure flexwnd doesn't do ANYTHING with our mouse messages
- case WM_MOUSEMOVE:
- case WM_LBUTTONUP:
- case WM_LBUTTONDOWN:
- case WM_RBUTTONUP:
- case WM_RBUTTONDOWN:
- case WM_LBUTTONDBLCLK:
- case WM_RBUTTONDBLCLK:
- {
- POINT point = {int(signed short(LOWORD(lParam))), int(signed short(HIWORD(lParam)))};
- m_point = point;
- m_code = HitTest(point);
- }
- case WM_TIMER:
- case WM_CAPTURECHANGED:
- break;
- default:
- return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
- }
-
- switch (msg)
- {
- case WM_LBUTTONDOWN:
- case WM_LBUTTONDBLCLK:
- if (m_code == SB_ENDSCROLL)
- goto endscroll;
- if (m_code == SB_THUMBTRACK)
- m_bDragging = TRUE;
- else
- SetTimer(m_hWnd, 1, 500, NULL);
- m_startcode = m_code;
- m_startpoint = m_point;
- m_nPreDragPos = m_nPos;
- m_bCapture = TRUE;
- SetCapture();
- if (!m_bDragging)
- Notify(m_code);
- break;
-
- case WM_LBUTTONUP:
- case WM_MOUSEMOVE:
- if (!m_bDragging)
- break;
- if (Calc())
- {
- Invalidate();
- // Force repaint the updated scrollbar position. If we don't do this,
- // the WM_PAINT message will be pre-empted by the WM_FLEXVSCROLL messages.
- // Sometimes this happens during the entire duration of draggin the scroll
- // bar. The result is that the scroll bar does not get updated when
- // dragging.
- SendMessage(m_hWnd, WM_PAINT, 0, 0);
- }
- Notify(m_startcode);
- break;
-
- case WM_TIMER:
- if (m_bCapture) switch (wParam)
- {
- case 1:
- KillTimer(m_hWnd, 1);
- SetTimer(m_hWnd, 2, 50, NULL);
- case 2:
- if (m_bDragging)
- break;
- if (m_code == m_startcode)
- Notify(m_code);
- break;
- }
- break;
- }
-
- switch (msg)
- {
- case WM_LBUTTONUP:
- case WM_CAPTURECHANGED:
- endscroll:
- if (m_bCapture)
- {
- m_bCapture = FALSE;
- KillTimer(m_hWnd, 1);
- KillTimer(m_hWnd, 2);
- ReleaseCapture();
- if (m_bDragging)
- Notify(SB_THUMBPOSITION);
- BOOL bWasDragging = m_bDragging;
- m_bDragging = FALSE;
- if (bWasDragging)
- {
- if (Calc())
- Invalidate();
- }
- Notify(SB_ENDSCROLL);
- }
- break;
- }
-
- return 0;
- }
-
- void CFlexScrollBar::Notify(int code)
- {
- if (!m_hWndNotify)
- return;
-
- SendMessage(m_hWndNotify, m_bVert ? WM_FLEXVSCROLL : WM_FLEXHSCROLL,
- (WPARAM)code, (LPARAM)(LPVOID)this);
- }
-
- int CFlexScrollBar::HitTest(POINT point)
- {
- if (InRect(m_rectLineUp, point))
- return SB_LINEUP;
- else if (InRect(m_rectLineDown, point))
- return SB_LINEDOWN;
- else if (InRect(m_rectThumb, point))
- return SB_THUMBTRACK;
- else if (InRect(m_rectPageUp, point))
- return SB_PAGEUP;
- else if (InRect(m_rectPageDown, point))
- return SB_PAGEDOWN;
- else
- return SB_ENDSCROLL;
- }
-
- void CFlexScrollBar::SetColors(COLORREF bk, COLORREF fill, COLORREF line)
- {
- m_rgbBk = bk;
- m_rgbFill = fill;
- m_rgbLine = line;
- Invalidate();
- }
-